/**
 * 
 */
package fr.cephb.joperon.webapp.tag;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.jstl.core.LoopTagSupport;

import com.sleepycat.db.DatabaseException;


import fr.cephb.joperon.core.OperonDB;
import fr.cephb.joperon.core.OperonDB.KeyValueIterator;
import fr.cephb.util.Pair;

/**
 * @author lindenb
 * cf. com.tonbeller.wcf.pagestack.PageStackTag
 */
public class OperonDBLoopTag extends LoopTagSupport
	{
	private static final long serialVersionUID = 1L;
	private static final int DEFAULT_LIMIT=100000;
	private static enum RETURN_TYPE{ KEY,VALUE,BOTH};
	private String dbName=null;
	private RETURN_TYPE returnType=RETURN_TYPE.BOTH;
	private String start=null;
	private String end=null;
	private boolean includeEnd=true;
	private String key=null;
	private Iterator<?> iterator;
	private int startIndex=0;
	private int limit=DEFAULT_LIMIT;
	
	public void setDbName(String dbName)
		{
		this.dbName=dbName;
		}

	public void setFrom(String from) {
		this.start = from;
		}
	
	public void setTo(String to) {
		this.end = to;
		}
	
	public void setKey(String key) {
		this.key = key;
		}
	
	public void setIncludeEnd(boolean includeEnd) {
		this.includeEnd = includeEnd;
		}	
	
	public void setType(String s)
		{
		s=s.toLowerCase();
		returnType=RETURN_TYPE.BOTH;
		for(RETURN_TYPE t: RETURN_TYPE.values())
			{
			if(s.equals(t.name().toLowerCase()))
				{
				this.returnType=t;
				break;
				}
			}
		
		}
	
	public void setStartIndex(int startIndex) {
		this.startIndex = startIndex;
		}
	
	public void setLimit(int limit) {
		this.limit = limit;
		}
	
	@SuppressWarnings("unchecked")
	@Override
	protected void prepare() throws JspTagException
		{
		if(dbName==null) throw new JspTagException("dbName is null");
		Object o= super.pageContext.getAttribute(this.dbName,PageContext.REQUEST_SCOPE);
		if(o==null) throw new JspTagException("cannot get context["+dbName+"] is null");
		if(!(o instanceof OperonDB<?, ?>)) throw new JspTagException("["+dbName+"] is not an operon db");
		OperonDB database= OperonDB.class.cast(o);
		
		KeyValueIterator kviterator=null;
		try
			{
			if(start!=null && end!=null)
				{
				kviterator = database.listKeyValues(
						database.keyFromString(start),
						database.keyFromString(end),
						this.includeEnd);
				}
			else if(this.key!=null)
				{
				kviterator = database.listKeyValues(
						database.keyFromString(key),
						database.keyFromString(key),
						true);
				}
			else
				{
				throw new JspTagException("start/end/key undefined");
				}
			int index=-1;
			List<Object> list = new ArrayList<Object>();
			while(kviterator.hasNext()
				 && (index+1) < (this.startIndex+this.limit))
				{
				index++;
				o= kviterator.next();
				if(index< this.startIndex) continue;
				list.add(o);
				}
			kviterator.close();
			this.iterator= list.iterator();
			}
		catch(DatabaseException err)
			{
			throw new JspTagException(err);
			}
		finally
			{
			if(kviterator!=null) kviterator.close();
			}
		}
	@Override
	protected boolean hasNext() throws JspTagException {
		return this.iterator.hasNext();
		}

	/* (non-Javadoc)
	 * @see javax.servlet.jsp.jstl.core.LoopTagSupport#next()
	 */
	@Override
	protected Object next() throws JspTagException
		{
		Pair<?,?> pair= Pair.class.cast(this.iterator.next());
		switch(this.returnType)
			{
			case BOTH: return pair;
			case KEY: return pair.first();
			case VALUE: return pair.second();
			}
		return null;
		}

	@Override
	public void release() {
		super.release();
		iterator=null;
		this.returnType=RETURN_TYPE.BOTH;
		this.dbName=null;
		this.startIndex=0;
		this.start=null;
		this.end=null;
		this.limit=DEFAULT_LIMIT;
		this.includeEnd=true;
		}
	

}
